package com.weng.cloud.commons.base;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;

/**
 * @Author: weng
 * @Date: 2022/2/18
 * @Description: com.weng.cloud.commons
 * <p>
 * TODO: equals、hashCode、copyProperties
 * TODO: com.weng.cloud.commons.ext.spring.BeanUtil extends ObjectUtil
 *      getBean
 */

public class ObjectUtil {

    /*
        clone
     */

    private static <T> T deepCloneByJson(Object obj, Class<T> clz) {
        return JsonUtil.toObject(JsonUtil.toText(obj), clz);
    }

    private static <T> T deepCloneByIo(T src) throws IOException, ClassNotFoundException {
        Object obj = null;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        try {
            objectOutputStream.writeObject(src);
            obj = objectInputStream.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            objectInputStream.close();
            objectOutputStream.close();
        }
        return (T) obj;
    }

    public static <T> T deepClone(Object obj, Class<T> clz) {
        return deepCloneByJson(obj, clz);
    }

    /*
        present
     */

    public static <T> boolean isCollectionNullOrEmpty(Collection<T> collection) {
        return isObjectNull(collection) || collection.isEmpty();
    }

    public static boolean isStringNullOrBlank(String string) {
        return isObjectNull(string) || string.trim().length() == 0;
    }

    public static boolean isObjectNull(Object object) {
        return object == null;
    }

    public static <T> boolean notCollectionNullOrEmpty(Collection<T> collection) {
        return !isCollectionNullOrEmpty(collection);
    }

    public static boolean notStringNullOrBlank(String string) {
        return !isStringNullOrBlank(string);
    }

    public static boolean notObjectNull(Object object) {
        return !isObjectNull(object);
    }

    public static <K, V> boolean isMapNullOrEmpty(Map<K, V> map) {
        return isObjectNull(map) || map.isEmpty();
    }

    public static <K, V> boolean notMapNullOrEmpty(Map<K, V> map) {
        return !isMapNullOrEmpty(map);
    }

    public static <T> boolean isArrayNullOrEmpty(T[] array) {
        return isObjectNull(array) || array.length == 0;
    }

    public static <T> boolean notArrayNullOrEmpty(T[] array) {
        return !isArrayNullOrEmpty(array);
    }

    /*
        java.util.Optional
     */

    public static <T> Optional<T> optionalOf(T t) {
        return Optional.ofNullable(t);
    }

    /*
        reflect
     */

    protected static Field[] obtainFields(Class clz, boolean accessible, boolean included, String... fieldNames) {
        Field[] fields = clz.getDeclaredFields();
        if (isArrayNullOrEmpty(fields)) {
            return null;
        }
        return Arrays.stream(fields).peek(field -> field.setAccessible(accessible)
        ).filter(field -> {
            if (isArrayNullOrEmpty(fieldNames)) {
                return true;
            } else {
                return included ? StreamUtil.including(fieldNames, field.getName()) : StreamUtil.excluding(fieldNames, field.getName());
            }
        }).toArray(Field[]::new);
    }

    protected static <T> Field obtainField(T t, String fieldName, boolean accessible) throws NoSuchFieldException {
        Class clz = t.getClass();
        Field field = clz.getDeclaredField(fieldName);
        field.setAccessible(accessible);
        return field;
    }

    public static <T> void setFieldValue(T t, String fieldName, Object fieldValue) throws NoSuchFieldException, IllegalAccessException {
        Field field = obtainField(t, fieldName, true);
        field.set(t, fieldValue);
    }

    public static <T, E> E getFieldValue(T t, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = obtainField(t, fieldName, true);
        return (E) field.get(t);
    }

    public static <T> Class getFieldClass(T t, String fieldName) throws NoSuchFieldException {
        Field field = obtainField(t, fieldName, false);
        return field.getType();
    }

    protected static String[] obtainFieldNames(Class clz, boolean includes, String... fieldsNames) {
        Field[] fields = obtainFields(clz, false, includes, fieldsNames);
        if (isArrayNullOrEmpty(fields)) {
            return null;
        }
        return Arrays.stream(fields).map(Field::getName).toArray(String[]::new);
    }
}
